#include <opengl/camera.h>
#include <cmath>
#include <utility>

namespace opengl
{

// 使用向量构造
Camera::Camera(const glm::vec3& position, const glm::vec3& up, float yaw, float pitch):
    _position(position),
    _nextPosition(position),
    _front(glm::vec3(0.0f, 0.0f, -1.0f)), 
    _up(),
    _right(),
    _worldUp(std::move(up)),
    _yaw(yaw),
    _pitch(pitch),
    _moveSpeed(SPEED), 
    _moveSensitivity(SENSITIVITY)
{
    this->update();
}

// 键盘控制
glm::vec3 Camera::horizontalMove(CameraMovement direction, float deltaTime, float raduis)
{
    float velocity = this->_moveSpeed * deltaTime;
    glm::vec3 hfront(cos(glm::radians(this->_yaw)), 0.0f, sin(glm::radians(this->_yaw)));
    glm::vec3 hright = glm::normalize(glm::cross(hfront, this->_worldUp));

    if (direction == FORWARD)
    {
        this->_nextPosition = this->_position + hfront * velocity;
        return this->_nextPosition + hfront * raduis;
    }
    else if (direction == BACKWARD)
    {
        this->_nextPosition = this->_position - hfront * velocity;
        return this->_nextPosition - hfront * raduis;
    }
    else if (direction == LEFT)
    {
        this->_nextPosition = this->_position - hright * velocity;
        return this->_nextPosition - hright * raduis;
    }
    else if (direction == RIGHT)
    {
        this->_nextPosition = this->_position + hright * velocity;
        return this->_nextPosition + hright * raduis;
    }
    return glm::vec3(0.0f, 0.0f, 0.0f);
}

// 鼠标控制
void Camera::changeSight(float xoffset, float yoffset, bool constrainPitch)
{
    xoffset *= this->_moveSensitivity;
    yoffset *= this->_moveSensitivity;

    this->_yaw += xoffset;
    this->_pitch += yoffset;

    if (constrainPitch)
    {
        if (this->_pitch > 80.0f)
            this->_pitch  = 80.0f;
        if (this->_pitch  < -88.0f)
            this->_pitch  = -88.0f;
    }

    this->update();
}

// 更新摄像机状态
void Camera::update()
{
    glm::vec3 front;
    front.x = cos(glm::radians(this->_yaw)) * cos(glm::radians(this->_pitch));
    front.y = sin(glm::radians(this->_pitch));
    front.z = sin(glm::radians(this->_yaw)) * cos(glm::radians(this->_pitch));
    this->_front = glm::normalize(front);

    this->_right = glm::normalize(glm::cross(this->_front, this->_worldUp));
    this->_up = glm::normalize(glm::cross(this->_right, this->_front));
}

}